﻿#region Copyright
//       Copyright (c) 2009 - Antoine Jaussoin - http://www.jaussoin.com/blog/
// 
//        This program is free software: you can redistribute it and/or modify
//        it under the terms of the GNU General Public License as published by
//        the Free Software Foundation, either version 3 of the License, or
//        (at your option) any later version.
// 
//        This program is distributed in the hope that it will be useful,
//        but WITHOUT ANY WARRANTY; without even the implied warranty of
//        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//        GNU General Public License for more details.
// 
//        You should have received a copy of the GNU General Public License
//        along with this program.  If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;

namespace FT.Architecture.Example.Console.Benchmark
{
    public abstract class BenchmarkBase
    {
        /// <summary>
        /// Return all the pets
        /// </summary>
        /// <returns></returns>
        protected abstract IList<BenchmarkItem> RunInternal(BenchmarkTimer timer);

        /// <summary>
        /// Code that is run before run
        /// </summary>
        protected abstract void BeforeRun();

        /// <summary>
        /// Code that is run after run
        /// </summary>
        protected abstract void AfterRun();

        /// <summary>
        /// Benchmark name
        /// </summary>
        public abstract string Name { get; }

        /// <summary>
        /// Benchmark description
        /// </summary>
        public abstract string Description { get; }

        /// <summary>
        /// Run the benchmark
        /// </summary>
        public BenchmarkResult RunBenchmark()
        {
            //Always run the test twice first to ensure a hot start
            RunInternal(new BenchmarkTimer());
            RunInternal(new BenchmarkTimer());

            BenchmarkTimer timer = new BenchmarkTimer();
            IList<BenchmarkItem> returnedObjects = RunInternal(timer);
            timer.Stop();

            return new BenchmarkResult(returnedObjects, timer);
        }
    }

    public class BenchmarkTimer
    {
        private readonly DateTime _start;
        private DateTime _end;
        private readonly IList<BenchmarkEvent> _events;

        public BenchmarkTimer()
        {
            _start = DateTime.Now;
            _events = new List<BenchmarkEvent>();
            _events.Add(new BenchmarkEvent("-- Start --",_start, _start, _start));
        }

        public void ReportEvent(string evt)
        {
            _events.Add(new BenchmarkEvent(evt, DateTime.Now, _events[_events.Count - 1].Date, _start));
        }

        public void Stop()
        {
            _end = DateTime.Now;
            _events.Add(new BenchmarkEvent("-- Stop --", _end, _events[_events.Count - 1].Date, _start));
        }

        public DateTime Start
        {
            get { return _start; }
        }

        public DateTime End
        {
            get { return _end; }
        }

        public IList<BenchmarkEvent> Events
        {
            get { return _events; }
        }

        public double TotalElapsed
        {
            get
            {
                return (_end - _start).TotalMilliseconds;
            }
        }
    }

    public struct BenchmarkEvent
    {
        private readonly string _eventDescription;
        private readonly DateTime _date;
        private readonly DateTime _lastEvent;
        private DateTime _start;

        public BenchmarkEvent(string eventDescription, DateTime date, DateTime lastEvent, DateTime start)
        {
            _eventDescription = eventDescription;
            _date = date;
            _lastEvent = lastEvent;
            _start = start;
        }

        public string EventDescription
        {
            get { return _eventDescription; }
        }

        public DateTime Date
        {
            get { return _date; }
        }

        public double ElapsedSinceLastEvent
        {
            get
            {
                return (_date - _lastEvent).TotalMilliseconds;
            }
        }

        public double ElapsedSinceStart
        {
            get
            {
                return (_date - _start).TotalMilliseconds;
            }
        }
    }

    public struct BenchmarkResult
    {
        private readonly IList<BenchmarkItem> _items;
        private readonly BenchmarkTimer _timer;

        public BenchmarkResult(IList<BenchmarkItem> items, BenchmarkTimer timer)
        {
            _items = items;
            _timer = timer;
        }

        public IList<BenchmarkItem> Items
        {
            get { return _items; }
        }

        public BenchmarkTimer Timer
        {
            get { return _timer; }
        }
    }

    public struct BenchmarkItem
    {
        private readonly object _field1;
        private readonly object _field2;
        private readonly object _field3;

        public BenchmarkItem(object field1, object field2, object field3)
        {
            _field1 = field1;
            _field2 = field2;
            _field3 = field3;
        }

        public object Field1
        {
            get { return _field1; }
        }

        public object Field2
        {
            get { return _field2; }
        }

        public object Field3
        {
            get { return _field3; }
        }
    }
}
